/*
 * SPDX-License-Identifier: Apache-2.0
 * Copyright Blazebit
 */

package com.blazebit.persistence.view.impl.accessor;

import com.blazebit.persistence.view.impl.EntityViewManagerImpl;
import com.blazebit.persistence.view.spi.type.DirtyTracker;
import com.blazebit.persistence.view.impl.metamodel.AbstractMethodAttribute;
import com.blazebit.persistence.view.spi.type.DirtyStateTrackable;
import com.blazebit.persistence.view.spi.type.MutableStateTrackable;
import com.blazebit.persistence.view.metamodel.BasicType;
import com.blazebit.persistence.view.metamodel.MethodAttribute;
import com.blazebit.persistence.view.metamodel.SingularAttribute;
import com.blazebit.persistence.view.spi.type.BasicUserType;

/**
 *
 * @author Christian Beikov
 * @since 1.2.0
 */
public final class DirtyStateViewAttributeAccessor extends ViewAttributeAccessor implements InitialValueAttributeAccessor {

    private final int dirtyStateIndex;
    private final boolean updatableOnly;
    private final BasicUserType<Object> userType;

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public DirtyStateViewAttributeAccessor(EntityViewManagerImpl evm, MethodAttribute<?, ?> attribute) {
        super(evm, attribute, false);
        this.dirtyStateIndex = ((AbstractMethodAttribute<?, ?>) attribute).getDirtyStateIndex();
        this.updatableOnly = ((AbstractMethodAttribute<?, ?>) attribute).isUpdatableOnly() && !attribute.isCorrelated();
        if (attribute instanceof SingularAttribute<?, ?>) {
            SingularAttribute<?, ?> singularAttribute = (SingularAttribute<?, ?>) attribute;
            if (singularAttribute.getType() instanceof BasicType<?>) {
                userType = ((BasicType) singularAttribute.getType()).getUserType();
            } else {
                userType = null;
            }
        } else {
            userType = null;
        }
    }

    @Override
    public Object getMutableStateValue(Object view) {
        if (view instanceof MutableStateTrackable) {
            return ((MutableStateTrackable) view).$$_getMutableState()[dirtyStateIndex];
        } else {
            return null;
        }
    }

    @Override
    public Object getInitialValue(Object view) {
        if (view instanceof DirtyStateTrackable) {
            return ((DirtyStateTrackable) view).$$_getInitialState()[dirtyStateIndex];
        } else {
            return null;
        }
    }

    @Override
    public void setInitialValue(Object view, Object initialValue) {
        if (view instanceof DirtyStateTrackable) {
            ((DirtyStateTrackable) view).$$_getInitialState()[dirtyStateIndex] = initialValue;
        }
    }

    @Override
    public void setValue(Object view, Object value) {
        super.setValue(view, value);
        if (view instanceof MutableStateTrackable) {
            MutableStateTrackable mutableStateTrackable = (MutableStateTrackable) view;
            if (updatableOnly && value instanceof MutableStateTrackable) {
                ((MutableStateTrackable) value).$$_addReadOnlyParent(mutableStateTrackable, dirtyStateIndex);
            } else if (value instanceof DirtyTracker) {
                ((DirtyTracker) value).$$_setParent(mutableStateTrackable, dirtyStateIndex);
            }
            mutableStateTrackable.$$_getMutableState()[dirtyStateIndex] = value;
            if (view instanceof DirtyStateTrackable) {
                if (userType != null) {
                    ((DirtyStateTrackable) view).$$_getInitialState()[dirtyStateIndex] = userType.deepClone(value);
                } else {
                    ((DirtyStateTrackable) view).$$_getInitialState()[dirtyStateIndex] = value;
                }
            }
        }
    }
}
